package net.funafuna.funamail.imap4;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class Imap4Client {

    private static final String CRLF = "\r\n";

    private final String host;
    private final int port;
    private final boolean useSSL;

    private int counter = 0;

    private Socket socket;
    private BufferedReader br;
    private BufferedWriter bw;

    private String separator = ".";

    public Imap4Client(String host, int port, boolean useSSL) {
        this.host = host;
        this.port = port;
        this.useSSL = useSSL;
    }

    public void connect() throws Exception {
        if (this.useSSL) {
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, null, null);
            SSLSocketFactory sf = context.getSocketFactory();
            this.socket = sf.createSocket(this.host, this.port);
        } else {
            this.socket = new Socket(this.host, this.port);
        }

        final InputStream is = this.socket.getInputStream();
        this.br = new BufferedReader(new InputStreamReader(is));

        final OutputStream os = this.socket.getOutputStream();
        this.bw = new BufferedWriter(new OutputStreamWriter(os));
    }

    public void disconnect() throws Exception {
        this.socket.close();
    }

    public void login(final String username, final String password) throws Exception {
        {
            write(++this.counter + " capability");
            write(CRLF);
            flush();
            final Imap4Response res = getResponse();
            System.out.println(res);
        }
        {
            write(++this.counter + " login ");
            write("\"" + username + "\" ");
            write("\"" + password + "\" ");
            write(CRLF);
            flush();
            final Imap4Response res = getResponse();
            System.out.println(res);
        }

        {
            write(++this.counter + " NAMESPACE");
            write(CRLF);
            flush();
            final Imap4Response res = getResponse();
            System.out.println(res);
        }
    }

    public String[] getFolderList() throws Exception {
        write(++this.counter + " LIST \"\"*");
        write(CRLF);
        flush();
        final Imap4Response res = getResponse();
        System.out.println(res);

        final List<String> folderList = new ArrayList<String>();
        final String[] list = res.getOptionalMessage();
        for (final String folder : list) {
            String temp = folder.substring(folder.indexOf('"') + 1);
            temp = temp.substring(temp.indexOf('"') + 1);
            temp = temp.substring(temp.indexOf('"') + 1, temp.length() - 1);
            folderList.add(temp);
            System.out.println(temp);
        }
        return folderList.toArray(new String[folderList.size()]);
    }

    public String[] getUidList(final String folderName) throws Exception {
        {
            write(++this.counter + " SELECT \"" + folderName + "\"");
            write(CRLF);
            flush();
            final Imap4Response res = getResponse();
            System.out.println(res);
        }

        {
            write(++this.counter + " UID SEARCH ALL");
            write(CRLF);
            flush();
            final Imap4Response res = getResponse();
            System.out.println(res);

            final String[] list = res.getOptionalMessage();
            String temp = list[0];
            temp = temp.substring(7);
            final String[] uidList = temp.split(" ");
            System.out.println(Arrays.toString(uidList));

            return uidList;
        }
    }

    public void logout() throws Exception {
        write(++this.counter + " LOGOUT");
        write(CRLF);
        flush();
        final Imap4Response res = getResponse();
        System.out.println(res);
    }

    private Imap4Response getResponse() throws Imap4Exception, IOException {
        final List<String> list = new ArrayList<String>();
        while (true) {
            final String message = this.br.readLine();
            if (message == null) {
                throw new Imap4Exception("message is null.");
            }
            if (message.length() < 4) {
                throw new Imap4Exception("message is invalid. message=[" + message + "]");
            }
            if (message.matches("^\\d+ [a-zA-Z]+ .+")) {
                final int start1 = message.indexOf(' ') + 1;
                final int start2 = message.indexOf(' ', start1);
                final String code = message.substring(start1, start2);
                final String mes = message.substring(start2 + 1);
                return new Imap4Response(code, mes, list.toArray(new String[list.size()]));
            } else {
                list.add(message.substring(2));
                continue;
            }
        }
    }

    private void write(String message) throws IOException {
        this.bw.write(message);
    }

    private void flush() throws IOException {
        this.bw.flush();
    }

}
